<template>
    <div class="base-tree flex h-full w-full flex-col gap-[8px]">
        <div>
            <form @submit.prevent="onSearch">
                <el-input v-model="keyword" :suffix-icon="Search" clearable placeholder="请输入关键字" />
            </form>
        </div>
        <div class="auto-scrollbar h-0 flex-1 overflow-y-auto">
            <el-tree
                ref="treeRef"
                v-loading="loading"
                :expand-on-click-node="false"
                :filter-node-method="onFilterNode"
                :props="getTreeProps()"
                highlight-current
                node-key="id"
                v-bind="$attrs"
            >
                <template v-if="$slots.default" #default="scopedData">
                    <slot v-bind="scopedData"></slot>
                </template>
                <template v-else #default="scopedData">
                    <div class="tree-label" @click="$emit('node-label-click', scopedData)">
                        <template v-if="$slots.icon">
                            <slot name="icon" v-bind="scopedData"></slot>
                        </template>
                        <BaseIcon
                            v-else
                            :name="(scopedData.node.isLeaf ? leafIconName : branchIconName) as string"
                            :size="iconSize"
                        />
                        <span class="mx-[8px] truncate text-[#30333A]" v-text="scopedData.node.label"></span>
                    </div>
                </template>
            </el-tree>
        </div>
    </div>
</template>

<script lang="ts" setup>
/**
 * @ClassName BaseTree
 * @Description 带搜索的树组件
 * @Author xuyizhuo
 * @Date 2024/12/20 21:13
 * @example
 */
import { ref } from 'vue'
import { Search } from '@element-plus/icons-vue'
import { ElTree } from 'element-plus'
import type { TreeOptionProps } from 'element-plus/es/components/tree/src/tree.type'

// 组件参数
interface VueProps {
    props?: Record<string, string> // 组件显示的字段属性，同原生el-tree props
    loading?: boolean // 是否加载中
    iconSize?: number // 图标大小
    branchIconName?: string // 分支节点图标（中间节点，度不为0的节点）
    leafIconName?: string // 叶子节点图标
}

const vueProps = withDefaults(defineProps<VueProps>(), {
    props: () => {
        return {
            children: 'children',
            label: 'name',
        }
    },
    loading: false,
    iconSize: 16, // 图标大小
    branchIconName: 'tree-sort', // 分支节点默认图标
    leafIconName: 'docs', // 叶子节点默认图标
})
defineEmits(['node-label-click'])

const keyword = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()

// 默认的树属性设置
const defaultProps: TreeOptionProps = {
    children: 'children',
    label: 'name',
}
let mergeProps: TreeOptionProps // 合并后的树属性设置
const getTreeProps = () => {
    mergeProps = { ...defaultProps, ...vueProps.props } as TreeOptionProps
    return mergeProps
}

const onSearch = () => {
    treeRef.value!.filter(keyword.value)
}

// 树节点过滤
const onFilterNode = (value: string, data: AnyObject) => {
    if (!value) {
        return true
    }

    // return data?.name?.includes(value)
    // 动态从props中获取显示的字段
    return !!data?.[(mergeProps.label || defaultProps.label) as string]?.toUpperCase()?.includes(value?.toUpperCase())
}

defineExpose({
    treeRef,
})
</script>

<style lang="scss" scoped>
.base-tree {
    // 树的节点
    .tree-label {
        display: flex;
        align-items: center;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    :deep(.el-tree) {
        --el-tree-node-hover-bg-color: #ecf5ff;
        // 树节点
        .el-tree-node {
            // 树的内容
            .el-tree-node__content {
                margin-top: 4px;
                margin-bottom: 4px;
                border-radius: 4px;
                padding-top: 4px;
                padding-bottom: 4px;
                height: 32px;
            }

            &.is-current {
                // 树的内容-高亮
                & > .el-tree-node__content {
                    background: #ecf5ff;
                }
            }
        }
    }
}
</style>
